#include "Debug.h"
#include "EdgeBase.h"



EdgeBase::EdgeBase(int _index,  VertexBase* _start, VertexBase* _end, bool _border)
{
	start = _start;
	end = _end;
	index = _index;
	borderEdge = _border;
	setWeight();
}

EdgeBase::~EdgeBase(void)
{
}

double EdgeBase::getAngleInTriangle(VertexBase* third)
{
	MVector ca(third->getPos() - start->getPos());
	MVector cb(third->getPos() - end->getPos());
	double cAngle = ca.angle(cb) * 180 / M_PI;	
	return cAngle;
}

bool operator== (EdgeBase &cP1, EdgeBase &cP2)
{
	if(cP1.start == cP2.start && cP1.end == cP2.end)
		return true;
	if(cP1.end == cP2.start && cP1.start == cP2.end)
		return true;
	return false;
}

void EdgeBase::setWeight()
{
	double length = start->getPos().distanceTo(end->getPos());
	if(borderEdge)
		weight = 0.0001;
	else 
		weight = length;
}

double EdgeBase::getWeight()
{
	return weight;
}

int EdgeBase::getNum()
{
	return index;
}

bool EdgeBase::isBoundary()
{
	return borderEdge;
}

VertexBase* EdgeBase::getStart()
{
	return start;
}

VertexBase* EdgeBase::getEnd()
{
	return end;
}

void EdgeBase::addTriangle(TriangleBase* tri, VertexBase* opposite)
{
	 triangles[tri] = opposite;
	 alreadyProjected[tri] = false;
}

bool EdgeBase::alreadyProjectedTriangle(TriangleBase* in)
{
	try
	{
		return alreadyProjected.at(in);
	}
	catch (exception e)
	{
		return false;
	}
}


void EdgeBase::fixOppositeVertices(queue<EdgeBase::queueElement>* fixingQueue, TriangleBase* tri, VertexBase* start, VertexBase* end)
{
	TriangleBase* otherTri = getNextTriangle(tri);
	vector<EdgeBase*> otherEdges = tri->getSides();
	for(int j=0; j<otherEdges.size(); j++)
		if(otherEdges[j]->getNum() == index)
			otherEdges.erase(otherEdges.begin()+j);

	EdgeBase* a = otherEdges[0];
	EdgeBase* b = otherEdges[1];
	EdgeBase* c = this;
	VertexBase* A = a->getOpposite(tri);
	VertexBase* B = b->getOpposite(tri);
	VertexBase* C = triangles[tri];		

	if(C->isFixed(tri))
		return;

	if(A != start && B != end)
	{
		swap(a,b);
		swap(A,B);
	}	

	if(b->getNextTriangle(tri) != NULL && !alreadyProjectedTriangle(b->getNextTriangle(tri)))
	{
		queueElement q;
		q.edge = b;
		q.tri = b->getNextTriangle(tri);
		q.start = A;
		q.end = C;
		fixingQueue->push(q);
	}
	if(a->getNextTriangle(tri) != NULL && !alreadyProjectedTriangle(a->getNextTriangle(tri)))
	{
		queueElement q;
		q.edge = a;
		q.tri = a->getNextTriangle(tri);
		q.start = C;
		q.end = B;
		fixingQueue->push(q);
	}
	if(c->getNextTriangle(tri) != NULL && !alreadyProjectedTriangle(c->getNextTriangle(tri)))
	{
		queueElement q;
		q.edge = c;
		q.tri = c->getNextTriangle(tri);
		q.start = B;
		q.end = A;
		fixingQueue->push(q);
	}

	////////////Now start fixing vertices///////////
	
	if(otherTri != NULL)
	{
		A->setUVPoint(A->getUVPoint(otherTri).u, A->getUVPoint(otherTri).v, tri);
		B->setUVPoint(B->getUVPoint(otherTri).u, B->getUVPoint(otherTri).v, tri);
	}
	
	double length = sqrt( (pow(A->getUVPoint(tri).u - B->getUVPoint(tri).u, 2)) + (pow(A->getUVPoint(tri).v - B->getUVPoint(tri).v, 2))) ;
	double Anum = A->getAngle(tri);
	double Bnum = B->getAngle(tri);		
	double Cnum = C->getAngle(tri);
	
	double dUp = ( length * sin(Anum*M_PI/180) * sin(Bnum*M_PI/180) ) / sin((Anum + Bnum)*M_PI/180);
	double dAlong = dUp / tan(Anum*M_PI/180);	

	if(dAlong < 0.0000001 && dAlong > -0.0000001)
		dAlong = 0;

	MPoint AUvAsMPoint(A->getUVPoint(tri).u, A->getUVPoint(tri).v, 0);
	MPoint BUvAsMPoint(B->getUVPoint(tri).u, B->getUVPoint(tri).v, 0);
	MVector AB = (BUvAsMPoint - AUvAsMPoint);
	AB.normalize();
	double comps[3];
	AB.get(comps);			
	MVector ABCross(-1*comps[1], comps[0], 0);
	ABCross.normalize();		
	MPoint final = AUvAsMPoint + dAlong*AB + dUp*ABCross;
		

	if(abs(final.x) < 0.00001)
		final.x =0;
	if(abs(final.y) < 0.00001)
		final.y =0;

	if( a->alreadyProjectedTriangle(a->getNextTriangle(tri)) && !a->isBoundary() )
		C->averageOutPositions(tri, a->getNextTriangle(tri), final.x, final.y);
	else if( b->alreadyProjectedTriangle(b->getNextTriangle(tri)) && !b->isBoundary() )		
		C->averageOutPositions(tri, b->getNextTriangle(tri), final.x, final.y);
	else		
		C->setUVPoint(final.x, final.y, tri);	
	
	a->setAlreadyProjected(tri);
	b->setAlreadyProjected(tri);
	c->setAlreadyProjected(tri);	
}

VertexBase* EdgeBase::getOpposite(TriangleBase* tri)
{
	return triangles[tri];
}

void EdgeBase::print()
{
	cout << "Edge: " << index << " Boundary: " << borderEdge << " Start: " << start->getNum() << " End: " << end->getNum() <<"\tTriangles: ";
	for(map<TriangleBase*, VertexBase*>::iterator i = triangles.begin(); i!=triangles.end(); i++)		
		cout << i->first->getNum() << " ";
	cout << endl;
}

TriangleBase* EdgeBase::getNextTriangle(TriangleBase* in)
{
	if(borderEdge)
		return NULL;
	for(map<TriangleBase*, VertexBase*>::iterator i = triangles.begin(); i!=triangles.end(); i++)	
		if(i->first !=in)
			return i->first;
	return NULL;
}

void EdgeBase::setAlreadyProjected(TriangleBase* in)
{
	alreadyProjected[in] = true;
}